/*
 * This computer program is the confidential information and proprietary trade
 * secret of Apptonelabs, Inc. Possessions and use of this program must
 * conform strictly to the license agreement between the user and
 * Apptonelabs, Inc., and receipt or possession does not convey any rights
 * to divulge, reproduce, or allow others to use this program without specific
 * written authorization of Apptonelabs, Inc.
 * 
 * Copyright (c) 2012-2015 Apptonelabs, Inc. All Rights Reserved.
 */
package com.apptonelabs.vnc.vphone;

import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

class SessionRecorder {
	protected FileOutputStream f;
	protected DataOutputStream df;
	protected long startTime, lastTimeOffset;

	protected byte[] buffer;
	protected int bufferSize;
	protected int bufferBytes;

	public SessionRecorder(String name, int bufsize) throws IOException {
		f = new FileOutputStream(name);
		df = new DataOutputStream(f);
		startTime = System.currentTimeMillis();
		lastTimeOffset = 0;

		bufferSize = bufsize;
		bufferBytes = 0;
		buffer = new byte[bufferSize];
	}

	public SessionRecorder(String name) throws IOException {
		this(name, 65536);
	}

	//
	// Close the file, free resources.
	//

	public void close() throws IOException {
		try {
			flush();
		} catch (IOException e) {
		}

		df = null;
		f.close();
		f = null;
		buffer = null;
	}

	//
	// Write the FBS file header as defined in the rfbproxy utility.
	//

	public void writeHeader() throws IOException {
		df.write("FBS 001.000\n".getBytes());
	}

	//
	// Write one byte.
	//

	public void writeByte(int b) throws IOException {
		prepareWriting();
		buffer[bufferBytes++] = (byte) b;
	}

	//
	// Write 16-bit value, big-endian.
	//

	public void writeShortBE(int v) throws IOException {
		prepareWriting();
		buffer[bufferBytes++] = (byte) (v >> 8);
		buffer[bufferBytes++] = (byte) v;
	}

	//
	// Write 32-bit value, big-endian.
	//

	public void writeIntBE(int v) throws IOException {
		prepareWriting();
		buffer[bufferBytes] = (byte) (v >> 24);
		buffer[bufferBytes + 1] = (byte) (v >> 16);
		buffer[bufferBytes + 2] = (byte) (v >> 8);
		buffer[bufferBytes + 3] = (byte) v;
		bufferBytes += 4;
	}

	//
	// Write 16-bit value, little-endian.
	//

	public void writeShortLE(int v) throws IOException {
		prepareWriting();
		buffer[bufferBytes++] = (byte) v;
		buffer[bufferBytes++] = (byte) (v >> 8);
	}

	//
	// Write 32-bit value, little-endian.
	//

	public void writeIntLE(int v) throws IOException {
		prepareWriting();
		buffer[bufferBytes] = (byte) v;
		buffer[bufferBytes + 1] = (byte) (v >> 8);
		buffer[bufferBytes + 2] = (byte) (v >> 16);
		buffer[bufferBytes + 3] = (byte) (v >> 24);
		bufferBytes += 4;
	}

	//
	// Write byte arrays.
	//

	public void write(byte b[], int off, int len) throws IOException {
		prepareWriting();
		while (len > 0) {
			if (bufferBytes > bufferSize - 4)
				flush(false);

			int partLen;
			if (bufferBytes + len > bufferSize) {
				partLen = bufferSize - bufferBytes;
			} else {
				partLen = len;
			}
			System.arraycopy(b, off, buffer, bufferBytes, partLen);
			bufferBytes += partLen;
			off += partLen;
			len -= partLen;
		}
	}

	public void write(byte b[]) throws IOException {
		write(b, 0, b.length);
	}

	//
	// Flush the output. This method saves buffered data in the
	// underlying file object adding data sizes and timestamps. If the
	// updateTimeOffset is set to false, then the current time offset
	// will not be changed for next write operation.
	//

	public void flush(boolean updateTimeOffset) throws IOException {
		if (bufferBytes > 0) {
			df.writeInt(bufferBytes);
			df.write(buffer, 0, (bufferBytes + 3) & 0x7FFFFFFC);
			df.writeInt((int) lastTimeOffset);
			bufferBytes = 0;
			if (updateTimeOffset)
				lastTimeOffset = -1;
		}
	}

	public void flush() throws IOException {
		flush(true);
	}

	//
	// Before writing any data, remember time offset and flush the
	// buffer before it becomes full.
	//

	protected void prepareWriting() throws IOException {
		if (lastTimeOffset == -1)
			lastTimeOffset = System.currentTimeMillis() - startTime;
		if (bufferBytes > bufferSize - 4)
			flush(false);
	}

}
